﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace Commons.Extension
{
    /// <summary>
    /// Métodos de extensão para objetos COM.
    /// </summary>
    public static class ComObjectExtension
    {
        /// <summary>
        /// Recupera o tipo associado a um objeto COM.
        /// </summary>
        /// <param name="aComObject">Object COM que se deseja recuperar o tipo.</param>
        /// <param name="aAssembly">Assembly contendo os tipos COM onde procurar.</param>
        /// <returns>Tipo da interface do objeto COM ou nulo quando não encontrado.</returns>
        public static Type GetInterfaceType( this object aComObject, Assembly aAssembly  )
        {
            // Separa uma referência para a interface IUnknown do objeto COM.
            IntPtr lIUnknown = Marshal.GetIUnknownForObject( aComObject );

            // Separa do assembly apenas as interfaces com um GUID valido.
            var lEnmComTypes =
                from
                    sComType in aAssembly.GetTypes()
                where
                    sComType.GUID != Guid.Empty
                    &&
                    sComType.IsInterface
                select
                    sComType;

            // Itera pelas interfaces encontradas, buscando ao qual o objeto é convertivel
            foreach ( Type lComType in lEnmComTypes )
            {
                // Separa o GUID numa variavel, para poder passar por referencia.
                Guid lGuid = lComType.GUID;

                // Identifica se o objeto é suportado pela interface em questão.
                IntPtr lPtrInterface;
                // É nessa função que se identifica se o objeto é transformável para a interface em questão.
                Marshal.QueryInterface( lIUnknown, ref lGuid, out lPtrInterface );
                // Se retornar um ponteiro válido, é porque o objeto é transformavel e o tipo foi encontrado.
                if ( lPtrInterface != IntPtr.Zero )
                {
                    return lComType;
                }
            }

            return null;
        }
    }
}